// g++ testlibuv.cpp -luv
#include <uv.h>
#include <iostream>
#include <map>

#define BUFFER_SIZE 1024

std::map<int, std::string> fdmap;

void alloc_handler(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
    buf->base = (char *)malloc(BUFFER_SIZE);
    buf->len = BUFFER_SIZE;
}

void read_handler(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
    if (nread > 0)
    {
        std::cout.write(buf->base, nread);
        std::cout << std::endl;
        return;
    }

    std::cout << fdmap[stream->accepted_fd] << " disconnected" << std::endl;

    if (buf->base != nullptr)
        free(buf->base);

    uv_close((uv_handle_t *)stream, NULL);
    free(stream);
}

void connection_handler(uv_stream_t *server, int status)
{
    auto client = (uv_tcp_t *)malloc(sizeof(uv_tcp_t));
    uv_tcp_init(server->loop, client);

    uv_accept(server, (uv_stream_t *)client);

    sockaddr_in addr;
    int len;
    uv_tcp_getpeername(client, (sockaddr *)&addr, &len);

    char buf[25] = {0};
    uv_ip4_name(&addr, buf, 25);
    sprintf(buf, "%s:%d", buf, addr.sin_port);
    fdmap[client->accepted_fd] = buf;
    std::cout << buf << " connected" << std::endl;

    uv_read_start((uv_stream_t *)client, alloc_handler, read_handler);
}

int main()
{
    auto loop = uv_default_loop();

    uv_tcp_t tcp = {0};
    uv_tcp_init(loop, &tcp);

    sockaddr_in addr = {0};
    addr.sin_port = htons(7834);
    addr.sin_family = AF_INET;
    auto paddr = (sockaddr *)&addr;
    uv_tcp_bind(&tcp, paddr, 0);

    uv_listen((uv_stream_t *)&tcp, 5, connection_handler);

    return uv_run(loop, UV_RUN_DEFAULT);
}
